package com.youbff.task.core.task;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.log4j.Logger;
import org.quartz.SchedulerException;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.StepContribution;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;

import com.youbff.task.cluster.dto.ServerInfoDTO;
import com.youbff.task.cluster.service.ClusterService;
import com.youbff.task.constant.Constant;
import com.youbff.task.constant.TaskContext;
import com.youbff.task.core.TaskDispatch;
import com.youbff.task.core.base.LeaderCornYml;
import com.youbff.task.utils.LeaderUtils;
import com.youbff.task.utils.RedisUtil;
import com.youbff.task.utils.ShortHttpsClientUtil;

/**
 * 客户端任务
 * @author lixuefeng
 * @email youbff@163.com
 * @time 2018年1月29日 下午2:53:22
 */
public class ClientClusterTask implements Tasklet {
    private static final Logger logger = Logger.getLogger(ClientClusterTask.class);
    private static RedisUtil redisUtil;
    private static ClusterService clusterService;
    private static TaskDispatch taskDispatch;
    private static LeaderCornYml leaderCornYml;
    private static Job leaderClusterTask;
    
    @Override
    public RepeatStatus execute(StepContribution contribution,
            ChunkContext chunkContext) throws Exception {
        init();
        excHeartBeat();
        return RepeatStatus.FINISHED;
    }
    
    private void init(){
        if(null == redisUtil){
            redisUtil = TaskContext.getApplicationContext().getBean(RedisUtil.class);
        }
        if(null == clusterService){
            clusterService = TaskContext.getApplicationContext().getBean(ClusterService.class);
        }
        if(null == taskDispatch){
            taskDispatch = TaskContext.getApplicationContext().getBean(TaskDispatch.class);
        }
        if(null == leaderCornYml){
            leaderCornYml = TaskContext.getApplicationContext().getBean(LeaderCornYml.class);
        }
        if(null == leaderClusterTask){
            leaderClusterTask = (Job) TaskContext.getApplicationContext().getBean("leaderClusterTask");
        }
    }
    
    /**
     * 执行心跳
     * @author lixuefeng
     * @email youbff@163.com
     * @time 2018年1月30日 下午4:55:00
     */
    private void excHeartBeat(){
        //1.是否在进行选举
        ServerInfoDTO currentServer = TaskContext.getServerInfo();
        String serverMsg = currentServer.getSerIp()+":"+currentServer.getPort()+"/"+currentServer.getSerName();
        if(LeaderUtils.isVoteing()){
            logger.debug("有服务器在进行选举,"+serverMsg+"直接退出心跳服务！");
            return;
        }
        //2.判断leader是否存在
        if(!LeaderUtils.haveLeader()){
            logger.debug("当前时间无leader,"+serverMsg+"直接退出心跳服务！");
            return;
        }
        ServerInfoDTO leaderInfo = (ServerInfoDTO) redisUtil.get(Constant.leader_info_key);
        //3.如果自己为leader,判断leader是否启动，已经启动直接退出
        if(currentServer.getSerName().equals(leaderInfo.getSerName())){
            //本身为leader，判断领导任务是否启动
            try {
                if(taskDispatch.isExistJob(currentServer.getSerName())){
                    logger.debug("自己本身为leader,"+serverMsg+"直接退出心跳服务！");
                    return;
                }
            } catch (SchedulerException e) {
                logger.error("自己本身为leader,"+serverMsg+",leader 出现异常！", e);
                throw new RuntimeException();
            }
            logger.debug("自己本身为leader,"+serverMsg+"启动leader任务！");
            startMyLeader(currentServer);
            return;
        }
        String url = "http://"+leaderInfo.getSerIp()+":"+leaderInfo.getPort()+Constant.heartbeat_url;
        Map<String,String> params = new HashMap<String, String>();
        params.put("name", currentServer.getSerName());
        params.put("ip", currentServer.getSerIp());
        params.put("port", currentServer.getPort());
        String result = ShortHttpsClientUtil.sendHttpsPost(url, params, "utf-8");
        logger.debug("client:"+serverMsg+"与leader:"+leaderInfo.getSerName()+";心跳通讯结果:"+result);
        if(null != result){
            //通讯成功
            logger.debug("有服务器在进行选举,"+serverMsg+"直接退出心跳服务！");
            return ;
        }
        //4.通讯失败发起选举
        boolean voteLock = LeaderUtils.myVoteLeader();
        if(!voteLock){
            //没有拿到选举锁
            logger.debug("有服务器已经拿到选举权,"+serverMsg+"直接退出心跳服务！");
            return;
        }
        voteLeader(leaderInfo.getSerName());
    }
    
    /**
     * 进行leader选举
     * @author lixuefeng
     * @email youbff@163.com
     * @time 2018年1月30日 下午5:35:31
     */
    private void voteLeader(String removeLeaderKey){
        //1.取得所有客户端，选出领导
        Map<Object,Object> clientMap = redisUtil.getHashMap(Constant.client_info_cache_key);
        Object[] keys = clientMap.keySet().toArray(new Object[0]);  
        Random random = new Random();  
        Object randomKey = keys[random.nextInt(keys.length)];
        //被随机选出的领导
        ServerInfoDTO leaderInfo = (ServerInfoDTO) redisUtil.getHashMapValue(Constant.client_info_cache_key, (String)randomKey);
        List<String> removeCluster = new ArrayList<String>();
        removeCluster.add(removeLeaderKey);
        clusterService.clearClusterInfo(removeCluster);
        leaderInfo.setIsLeader(1);
        clusterService.updateClusterInfo(leaderInfo);
        //如果当前服务器为选举出的leader，启动
        startMyLeader(leaderInfo);
        //重置集群缓存信息
        resetClusterCatchInfo(leaderInfo);
    }
    
    /**
     * 重置集群缓存信息
     * @author lixuefeng
     * @email youbff@163.com
     * @time 2018年2月1日 下午2:26:03
     * @param leaderInfo
     */
    private void resetClusterCatchInfo(ServerInfoDTO leaderInfo){
        //重置leader信息
        redisUtil.set(Constant.leader_info_key, leaderInfo);
        //从集群客户端中移除当前服务器
        redisUtil.delHashMap(Constant.client_info_cache_key, leaderInfo.getSerName());
        redisUtil.delHashMap(Constant.client_heartbeat_cache_key, leaderInfo.getSerName());
        //移除选举锁
        redisUtil.remove(Constant.leader_vote_lock_key);
    }
    
    /**
     * 如果自己为leader，启动自己的leader job
     * @author lixuefeng
     * @email youbff@163.com
     * @time 2018年2月1日 下午2:40:09
     */
    private void startMyLeader(ServerInfoDTO voteLeader){
        ServerInfoDTO myServer = TaskContext.getServerInfo();
        String serverMsg = myServer.getSerIp()+":"+myServer.getPort()+"/"+myServer.getSerName();
        if(voteLeader.getSerName().equals(myServer.getSerName())){
            //自己为leader
            try {
                taskDispatch.addJob(myServer.getSerName(), leaderCornYml.getMain(), leaderClusterTask, new LeaderClusterTaskListener(myServer.getSerName()));
            } catch (SchedulerException e) {
                logger.error("当前服务器"+serverMsg+"启动leader任务失败", e);
                throw new RuntimeException();
            }
        }
    }
    
    
    
}
